package br.unifor.g2cl;

import java.lang.reflect.Method;
import java.net.SocketAddress;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.sf.jgcs.JGCSException;
import net.sf.jgcs.Message;
import net.sf.jgcs.Service;
import net.sf.jgcs.membership.MembershipSession;

/**
 * Implements remote method invocation in the group of processes.
 * 
 * @author
 */
public class RpcDispatcher extends MessageDispatcher implements
		MessageDispatcherListener {

	private static Logger logger = Logger.getLogger(RpcDispatcher.class
			.getName());

	private Object serverObject;
	
	private Marshaller reqMarshaller = null;

	
	public Marshaller getReqMarshaller() {
		return reqMarshaller;
	}

	public void setReqMarshaller(Marshaller reqMarshaller) {
		this.reqMarshaller = reqMarshaller;
	}


	public RpcDispatcher(IMarshalDataSession marshalDataSession,
			MembershipSession membershipSession, Service service,
			Object serverObject) throws JGCSException {

		super(marshalDataSession, membershipSession, service);
		setMessageDispatcherListener(this);
		this.serverObject = serverObject;
	}


	public RspList callRemoteMethod(MethodCall methodCall, int mode, int timeout) throws Exception {
		Message msg;
		msg = marshalDataSession.createMessage();
		msg.setSenderAddress(membershipSession.getLocalAddress());

		byte[] object = null;
		if (reqMarshaller != null) {
			object = reqMarshaller.getArrayFromObject(methodCall);
		} else {
			object = Util.getArrayFromObject(methodCall);
		}

		msg.setPayload(object);
		return castMessage(null, msg, mode, timeout);

	}

	public RspList callRemoteMethod(String method, Class[] paramTypes,
			Object[] args, int mode, int timeout) throws Exception {

		MethodCall methodCall = new MethodCall(method, paramTypes, args);
		return callRemoteMethod(methodCall, mode, timeout);
	}

	///////////////////////////////////////////////////////////////////////////


	public Object callRemoteMethod(SocketAddress target, MethodCall methodCall, int mode, int timeout)
	throws Exception {
		G2CLMessage msg;
		msg = (G2CLMessage) marshalDataSession.createMessage();
		msg.setSenderAddress(membershipSession.getLocalAddress());

		byte[] object = null;
		if (reqMarshaller != null) {
			object = reqMarshaller.getArrayFromObject(methodCall);
		} else {
			object = Util.getArrayFromObject(methodCall);
		}

		msg.setPayload(object);
		return sendMessage(msg, target, mode, timeout);
	}

	public Object callRemoteMethod(SocketAddress target, String method,
			Class[] paramTypes, Object[] args, int mode, int timeout)
	throws Exception {

		MethodCall methodCall = new MethodCall(method, paramTypes, args);
		return callRemoteMethod(target, methodCall, mode, timeout);
	}


	
	///////////////////////////////////////////////////////////////////////////
	
	public Object handle(G2CLMessage message) {
		try {
			byte[] messagePayload = message.getPayload();
			MethodCall methodCall = null;
			
			if (reqMarshaller != null) {
				methodCall = (MethodCall) reqMarshaller.getObjectFromByte(
						messagePayload, 0, messagePayload.length);	
			} else {
				methodCall = (MethodCall) Util.getObjectFromByte(
						messagePayload, 0, messagePayload.length);
			}
			
			Method m = serverObject.getClass().getMethod(methodCall.getName(),
					methodCall.getParamTypes());
			return m.invoke(serverObject, methodCall.getArgs());
			
		} catch (Exception e) {
			logger.log(Level.SEVERE, "error handling message", e);
		}
		return null;
	}

}
